package com.senseuni.da

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.ContentObserver
import android.database.Cursor
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import android.net.NetworkCapabilities
import android.net.Uri
import android.os.*
import android.provider.MediaStore
import android.view.View
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.elvishew.xlog.XLog
import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import com.jeremyliao.liveeventbus.LiveEventBus
import com.senseuni.da.databinding.ActivityMainBinding
import com.senseuni.da.db.AppDatabase
import com.senseuni.da.db.DbHelper
import com.senseuni.da.db.dao.MediaDao
import com.senseuni.da.db.entity.MediaEntity
import com.senseuni.da.db.entity.UpgradeEntity
import com.senseuni.da.events.*
import com.senseuni.da.http.ApiDataSource
import com.senseuni.da.iot.MQTTManager
import com.senseuni.da.iot.MqttInfo
import com.senseuni.da.model.LogType
import com.senseuni.da.model.NotificationItem
import com.senseuni.da.model.NotificationType
import com.senseuni.da.receiver.ConnectiveBroadcastReceiver
import com.senseuni.da.receiver.MainBroadcast
import com.senseuni.da.ui.MainContentFragment
import com.senseuni.da.ui.MainEmptyFragment
import com.senseuni.da.ui.SettingFragment
import com.senseuni.da.utils.*
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.lang.ref.WeakReference

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var dataSource: ApiDataSource

    private var mDefaultFragment: MainEmptyFragment? = null
    private var mContentFragment: MainContentFragment? = null
    private var mSettingFragment: SettingFragment? = null

    private lateinit var connectivityManager: ConnectivityManager
    private var networkCallback: NetworkCallback? = null
    private var connectivityReceiver: ConnectiveBroadcastReceiver? = null

    //初始化状态 0：初始化 1：正常初始化 2：初始化完成 -1：初始化失败
    private var initState = 0
    private var initRetryTime = 0

    private lateinit var mMediaDao: MediaDao

    private lateinit var mUIHandler: UIHandler

    private lateinit var mainBroadcast: MainBroadcast

    private class UIHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
        private val mOuter: WeakReference<MainActivity> = WeakReference(activity)
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            val activity = mOuter.get()
            when (msg.what) {
                MSG_HIDE_CONTROL -> {
                }
                MSG_INIT -> {
                    activity?.init()
                }
            }
        }
    }

    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.setBackgroundDrawableResource(R.color.black)
//        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or
//                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
//                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

//        val viewModel = ViewModelProvider(this)[MainViewModel::class.java]

        if (!EventBus.getDefault().isRegistered(this)) {
            XLog.d("register eventbus")
            EventBus.getDefault().register(this)
        }
        XLog.d("Lumx App onCreate>>>>>>>>>>>>>>>>" + BuildConfig.VERSION_NAME)
        val metrics = resources.displayMetrics
        XLog.d("WidthPixels: ${metrics.widthPixels}")
        XLog.d("HeightPixels: ${metrics.heightPixels}")
        XLog.d("Density: ${metrics.density}")
        XLog.d("ScaledDensity: ${metrics.scaledDensity}")
        XLog.d("DensityDpi: ${metrics.densityDpi}")
        initRetryTime = 0
//        metrics.densityDpi = 480
//        resources.updateConfiguration(Configuration(), metrics)

        mMediaDao = AppDatabase.getInstance().mediaDao()
        binding.imageEmpty = mMediaDao.count() == 0

        val fragmentManager = supportFragmentManager
        val fragmentTransaction = fragmentManager.beginTransaction()
        if (binding.imageEmpty == true) {
            mDefaultFragment = MainEmptyFragment.newInstance()
            fragmentTransaction.add(R.id.container_content, mDefaultFragment!!)
        } else {
            mContentFragment = MainContentFragment.newInstance()
            fragmentTransaction.add(R.id.container_content, mContentFragment!!)
        }
        fragmentTransaction.commit()

        ShellUtil.execSU("")

        SpUtil.saveQRCode("")

        dataSource = ApiDataSource.instance
        AppUtils.saveAppStartTime()
//        AppUtils.setVolume(AppUtils.getVolume())

        mUIHandler = UIHandler(this)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            requestPermission()
        }

        val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        contentResolver.registerContentObserver(imageUri, true, contentObserver)

        val intentFilter = IntentFilter()
        intentFilter.addAction(Intent.ACTION_TIME_TICK)
        intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
        mainBroadcast = MainBroadcast()
        registerReceiver(mainBroadcast, intentFilter)

        connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            registerNetworkCallback()
        } else {
            registerConnectivityReceiver()
        }

        LiveEventBus.get<MediaEntity>(MyEvent.KEY_IMAGE_ADD)
            .observe(this) {
                onMediaAdd(it)
            }

        val activeNetwork = connectivityManager.activeNetwork
        val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
        val isNetConnected =
            networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
        if (!isNetConnected) {
            XLog.d("Network is not connected")
            return
        }

        addLog(LogType.INFO, "App onCreate")
        init()
        checkUpgrade()

        val systemLanguage = LanguageHelper.getLanguage()
        XLog.d("current system language: $systemLanguage")
        if (systemLanguage != SpUtil.getLanguage()) {
            XLog.d("Language changed: $systemLanguage")
            SpUtil.saveLanguage(systemLanguage)
        }
    }

    private fun onMediaAdd(media: MediaEntity) {
        XLog.d("Add image: ${media.path}")
        if (media.mimeType == "video") {
            XLog.d("not supported video")
            return
        }
        showNotification(NotificationItem(getString(R.string.image_add_title), getString(R.string.image_add_desc), NotificationType.IMAGE_ADD))
        if (binding.imageEmpty == false) {
            mContentFragment?.addFirst(media)
        } else {
            binding.imageEmpty = false
            mDefaultFragment = null
            val fragmentManager = supportFragmentManager
            val fragmentTransaction = fragmentManager.beginTransaction()
            mContentFragment = MainContentFragment.newInstance()
            fragmentTransaction.replace(R.id.container_content, mContentFragment!!)
            fragmentTransaction.commit()
        }
    }

    private fun showNotification(notification: NotificationItem) {
        binding.notificationView.setTitle(notification.title)
        binding.notificationView.setSubTitle(notification.content)
        binding.notificationView.show()

        binding.notificationView.setOnClickListener {
            it.visibility = View.GONE
            when (notification.type) {
                NotificationType.IMAGE_ADD -> {
                }
                NotificationType.UPGRADE -> {
                    val id = notification.data as Int
                    val intent = Intent()
                    intent.setClass(this@MainActivity, UpgradeActivity::class.java)
                    intent.putExtra("id", id)
                    startActivity(intent)
                }
                else -> {
                    XLog.d("unknown notification type")
                }
            }
        }
    }


    private fun requestPermission() {
        XXPermissions.with(this)
            .permission(Permission.Group.STORAGE)
            .permission(Permission.ACCESS_FINE_LOCATION)
//            .permission(Permission.SYSTEM_ALERT_WINDOW)
//            .permission(Permission.REQUEST_INSTALL_PACKAGES)
            .request(object : OnPermissionCallback {
                override fun onGranted(permissions: List<String>, allGranted: Boolean) {
                    XLog.d("Permission is granted:" + permissions.toJson())
                }

                override fun onDenied(permissions: List<String>, doNotAskAgain: Boolean) {
                    if (doNotAskAgain) {
                        XLog.d("Storage permission onDenied,please manual setting")
                    } else {
                        XLog.d("get storage permission failed")
                    }
                }
            })
    }

    private fun init() {
        getDeviceInfo()
    }

    private fun getDeviceInfo() {
        dataSource.init()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<MqttInfo> {
                override fun onSubscribe(d: Disposable) {
                    XLog.d("start init")
                    initState = 1
                }

                override fun onNext(t: MqttInfo) {
                    XLog.d("init over")
                    initState = 2
                    if (isDestroyed) {
                        return
                    }
//                    autoSyncTimeAndZone()

                    MQTTManager.instance.init(t)
                    binding.root.setBackgroundColor(Color.BLACK)

                    mDefaultFragment?.onInitDone()
                    mContentFragment?.onInitDone()
                }

                override fun onError(e: Throwable) {
                    XLog.e("init error:%s", e.message)
                    initState = -1
                    addLog(LogType.ERROR, e.message ?: "init fail")
                    binding.root.setBackgroundColor(Color.BLACK)

                    val delay = 5000L + 5000 * (initRetryTime++)
                    XLog.d("after " + delay + "ms retry")
                    mUIHandler.sendEmptyMessageDelayed(MSG_INIT, delay)
                }

                override fun onComplete() {
                }
            })
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onTimeEvent(event: TimeEvent) {
        if (isDestroyed) {
            return
        }
        mContentFragment?.showTime()
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onMessageEvent(event: MessageEvent) {
        XLog.d(event.name)
//        if (event.isOver()) {
//            return
//        }
//        mAdapter.addItem(event.name)
//        uploadLog(LogType.INFO, event.name)
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onConnectivityEvent(event: ConnectivityEvent) {
        XLog.d("onConnectivityEvent")
        val activeNetwork = connectivityManager.activeNetwork
        val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
        val isNetConnected =
            networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
        if (!isNetConnected) {
            XLog.e("Network disconnected")
            addLog(LogType.ERROR, "Network is not connected")
            return
        }
        XLog.d("Network available")
        addLog(LogType.INFO, "Network available")
        if (initState <= 0) {
            init()
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onUpgradeEvent(event: UpgradeEvent) {
        XLog.d("UpgradeEvent")
        checkUpgrade()
    }

    override fun onPause() {
        super.onPause()
        XLog.d("onPause")
        addLog(LogType.INFO, "App onPause")
    }

    override fun onResume() {
        super.onResume()
        XLog.d("onResume")
        addLog(LogType.INFO, "App onResume")
    }

    private val contentObserver = object : ContentObserver(Handler()) {
        override fun onChange(selfChange: Boolean, uri: Uri?) {
            super.onChange(selfChange, uri)
            XLog.d("New image detected: $uri")
            try {
                uri?.let {
                    val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
                    val cursor: Cursor? =
                        contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, filePathColumn, null, null, MediaStore.Images.Media.DATE_ADDED + " DESC")
                    cursor?.let {
                        if (!cursor.moveToFirst()) {
                            cursor.close()
                            return
                        }
                        val columnIndex: Int = cursor.getColumnIndex(filePathColumn[0])
                        val picturePath: String = cursor.getString(columnIndex)
                        XLog.d("image path:${picturePath}")
                        cursor.close()
                    }
                }
            } catch (e: Exception) {
                XLog.e("content onchange error:" + e.message)
            }
        }

        override fun onChange(selfChange: Boolean) {
            super.onChange(selfChange)
            XLog.d("ImageContentObserver", "Media store changed.")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        XLog.d("onDestroy")
        addLog(LogType.INFO, "App onDestroy")

        contentResolver.unregisterContentObserver(contentObserver)

        mUIHandler.removeMessages(MSG_INIT)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            unRegisterNetworkCallback()
        } else {
            unRegisterConnectivityReceiver()
        }

        unregisterReceiver(mainBroadcast)

        EventBus.getDefault().unregister(this)
    }

    private fun checkUpgrade() {
        dataSource.checkUpgrade()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<UpgradeEntity> {
                override fun onSubscribe(d: Disposable) {}
                override fun onNext(entity: UpgradeEntity) {
                    XLog.d("found new version:%s", entity.version)
                    val notification = NotificationItem("Lumx有新的版本", "点击开始更新升级", NotificationType.UPGRADE)
                    notification.data = entity.id
                    showNotification(notification)
                }

                override fun onError(e: Throwable) {
//                    e.printStackTrace()
                    XLog.e("checkUpgrade fail:" + e.message)
                    addLog(LogType.ERROR, "checkUpgrade error: ${e.message}")
                }

                override fun onComplete() {}
            })
    }

    private fun addLog(logType: LogType, msg: String) {
        DbHelper.addAppLog(logType, msg)
    }

    @RequiresApi(Build.VERSION_CODES.N)
    private fun registerNetworkCallback() {
        networkCallback = object : NetworkCallback() {
            override fun onAvailable(network: Network) {
                super.onAvailable(network)
                XLog.d("Network available")
                addLog(LogType.INFO, "Network available")
                if (initState <= 0) {
                    init()
                }
            }

            override fun onLost(network: Network) {
                super.onLost(network)
                XLog.d("Network is lost")
                addLog(LogType.ERROR, "Network is lost")
            }

            override fun onCapabilitiesChanged(
                network: Network,
                capabilities: NetworkCapabilities
            ) {
                super.onCapabilitiesChanged(network, capabilities)
                if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
                    XLog.d("Internet is available")
//                    uploadLog(LogType.INFO, "Internet is available")
                } else {
                    XLog.d("Internet is not available")
//                    uploadLog(LogType.ERROR, "Internet is not available")
                }
            }
        }
        connectivityManager.registerDefaultNetworkCallback(networkCallback!!)
    }

    private fun registerConnectivityReceiver() {
        XLog.d("registerConnectivityReceiver")
        val intentFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        connectivityReceiver = ConnectiveBroadcastReceiver()
        registerReceiver(connectivityReceiver, intentFilter)
    }

    private fun unRegisterNetworkCallback() {
        networkCallback?.let { connectivityManager.unregisterNetworkCallback(it) }
    }

    private fun unRegisterConnectivityReceiver() {
        XLog.d("unRegisterConnectivityReceiver")
        connectivityReceiver?.let { unregisterReceiver(it) }
    }

    fun openSetting() {
        XLog.d("openSetting")
        onPause()

        val fragmentManager = supportFragmentManager
        val fragmentTransaction = fragmentManager.beginTransaction()
        mSettingFragment = SettingFragment.newInstance()
        fragmentTransaction.add(R.id.fl_container_setting, mSettingFragment!!)
        fragmentTransaction.commit()
    }

    fun settingClose() {
        XLog.d("settingClose")
        onResume()

        mSettingFragment?.let {
            val fragmentManager = supportFragmentManager
            val fragmentTransaction = fragmentManager.beginTransaction()
            fragmentTransaction.remove(it)
            fragmentTransaction.commit()
        }
    }

    fun showDefault() {
        XLog.d("showDefault")
        binding.imageEmpty = true
        mContentFragment = null
        val fragmentManager = supportFragmentManager
        val fragmentTransaction = fragmentManager.beginTransaction()
        mDefaultFragment = MainEmptyFragment.newInstance()
        fragmentTransaction.replace(R.id.container_content, mDefaultFragment!!)
        fragmentTransaction.commit()
    }

    companion object {
        private const val MSG_HIDE_CONTROL = 0x11
        private const val MSG_INIT = 0x12
    }

}